#!/bin/bash
# =============================================================================
# Ansible自动化运维系统 - 离线安装脚本
# 功能：在目标服务器上安装所有下载的依赖包
# 适用：CentOS 7 离线安装环境
# 作者：Ansible项目组
# 版本：1.0
# =============================================================================

set -e  # 遇到错误立即退出

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 日志函数
log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 配置变量
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OFFLINE_DIR="${SCRIPT_DIR}/../offline-packages"
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/ansible-offline-install-${DATE}.log"
BACKUP_DIR="/opt/ansible-backup-${DATE}"

# 检查root权限
check_root() {
    if [[ $EUID -ne 0 ]]; then
        log_error "此脚本需要root权限运行"
        exit 1
    fi
}

# 检查系统版本
check_system() {
    log_info "检查系统版本..."
    
    if [[ ! -f /etc/redhat-release ]]; then
        log_error "不支持的操作系统，此脚本仅支持CentOS 7"
        exit 1
    fi
    
    local version=$(rpm -q --queryformat '%{VERSION}' centos-release 2>/dev/null || echo "unknown")
    if [[ "$version" != "7" ]]; then
        log_warning "检测到CentOS版本: $version，建议使用CentOS 7"
    fi
    
    log_success "系统检查通过"
}

# 检查离线包完整性
check_offline_packages() {
    log_info "检查离线安装包完整性..."
    
    if [[ ! -d "$OFFLINE_DIR" ]]; then
        log_error "离线安装包目录不存在: $OFFLINE_DIR"
        exit 1
    fi
    
    # 检查必要目录
    local required_dirs=("rpms" "python-packages" "binaries" "configs")
    for dir in "${required_dirs[@]}"; do
        if [[ ! -d "$OFFLINE_DIR/$dir" ]]; then
            log_error "缺少必要目录: $OFFLINE_DIR/$dir"
            exit 1
        fi
    done
    
    # 验证校验和
    if [[ -f "$OFFLINE_DIR/checksums.md5" ]]; then
        log_info "验证文件完整性..."
        cd "$OFFLINE_DIR"
        if md5sum -c checksums.md5 --quiet; then
            log_success "文件完整性验证通过"
        else
            log_warning "部分文件校验失败，继续安装可能存在风险"
            read -p "是否继续安装？(y/N): " -n 1 -r
            echo
            if [[ ! $REPLY =~ ^[Yy]$ ]]; then
                exit 1
            fi
        fi
    else
        log_warning "未找到校验文件，跳过完整性验证"
    fi
    
    log_success "离线包检查完成"
}

# 备份现有配置
backup_configs() {
    log_info "备份现有配置..."
    
    mkdir -p "$BACKUP_DIR"
    
    # 备份重要配置文件
    local config_files=(
        "/etc/yum.repos.d"
        "/etc/ssh/sshd_config"
        "/etc/firewalld"
        "/etc/systemd/system"
        "/etc/crontab"
        "/etc/ansible"
    )
    
    for config in "${config_files[@]}"; do
        if [[ -e "$config" ]]; then
            cp -r "$config" "$BACKUP_DIR/" 2>/dev/null || true
            log_info "已备份: $config"
        fi
    done
    
    log_success "配置备份完成: $BACKUP_DIR"
}

# 配置本地YUM仓库
setup_local_repo() {
    log_info "配置本地YUM仓库..."
    
    # 备份原有仓库配置
    if [[ -d /etc/yum.repos.d ]]; then
        cp -r /etc/yum.repos.d "$BACKUP_DIR/yum.repos.d.backup"
    fi
    
    # 禁用所有在线仓库
    if [[ -d /etc/yum.repos.d ]]; then
        find /etc/yum.repos.d -name "*.repo" -exec mv {} {}.disabled \;
    fi
    
    # 创建本地仓库配置
    cat > /etc/yum.repos.d/ansible-offline.repo << EOF
[ansible-offline]
name=Ansible Offline Repository
baseurl=file://$OFFLINE_DIR/rpms
enabled=1
gpgcheck=0
priority=1
EOF
    
    # 清理YUM缓存
    yum clean all
    yum makecache
    
    log_success "本地YUM仓库配置完成"
}

# 安装RPM包
install_rpms() {
    log_info "安装RPM包..."
    
    # 安装基础包
    local base_packages=(
        "epel-release"
        "python3"
        "python3-pip"
        "python3-setuptools"
        "git"
        "wget"
        "curl"
        "tar"
        "gzip"
        "rsync"
    )
    
    for package in "${base_packages[@]}"; do
        log_info "安装包: $package"
        yum install -y "$package" || {
            log_warning "包 $package 安装失败，跳过"
        }
    done
    
    # 批量安装所有可用的RPM包
    log_info "批量安装其他RPM包..."
    yum install -y "$OFFLINE_DIR/rpms"/*.rpm || {
        log_warning "部分RPM包安装失败，继续后续步骤"
    }
    
    log_success "RPM包安装完成"
}

# 安装Python包
install_python_packages() {
    log_info "安装Python包..."
    
    # 确保pip可用
    if ! command -v pip3 &> /dev/null; then
        log_error "pip3未安装，请先安装python3-pip"
        return 1
    fi
    
    # 升级pip（使用离线包）
    local pip_package=$(find "$OFFLINE_DIR/python-packages" -name "pip-*.whl" | head -1)
    if [[ -n "$pip_package" ]]; then
        pip3 install --no-index --find-links "$OFFLINE_DIR/python-packages" --upgrade pip
    fi
    
    # 安装所有Python包
    log_info "安装Python包..."
    pip3 install --no-index --find-links "$OFFLINE_DIR/python-packages" "$OFFLINE_DIR/python-packages"/*.whl || {
        log_warning "部分Python包安装失败，尝试逐个安装"
        
        # 逐个安装
        for wheel in "$OFFLINE_DIR/python-packages"/*.whl; do
            if [[ -f "$wheel" ]]; then
                local package_name=$(basename "$wheel")
                log_info "安装: $package_name"
                pip3 install --no-index --find-links "$OFFLINE_DIR/python-packages" "$wheel" || {
                    log_warning "$package_name 安装失败，跳过"
                }
            fi
        done
    }
    
    # 安装tar.gz包
    for tarball in "$OFFLINE_DIR/python-packages"/*.tar.gz; do
        if [[ -f "$tarball" ]]; then
            local package_name=$(basename "$tarball")
            log_info "安装: $package_name"
            pip3 install --no-index --find-links "$OFFLINE_DIR/python-packages" "$tarball" || {
                log_warning "$package_name 安装失败，跳过"
            }
        fi
    done
    
    log_success "Python包安装完成"
}

# 安装二进制文件
install_binaries() {
    log_info "安装二进制文件..."
    
    cd "$OFFLINE_DIR/binaries"
    
    # 创建安装目录
    mkdir -p /opt/{prometheus,node_exporter,alertmanager,grafana}
    mkdir -p /usr/local/bin
    mkdir -p /etc/{prometheus,alertmanager,grafana}
    mkdir -p /var/lib/{prometheus,grafana}
    
    # 安装Prometheus
    if [[ -f prometheus-*.tar.gz ]]; then
        log_info "安装Prometheus..."
        tar -xzf prometheus-*.tar.gz
        local prometheus_dir=$(find . -maxdepth 1 -type d -name "prometheus-*" | head -1)
        if [[ -n "$prometheus_dir" ]]; then
            cp "$prometheus_dir"/{prometheus,promtool} /usr/local/bin/
            cp -r "$prometheus_dir"/{consoles,console_libraries} /etc/prometheus/
            chmod +x /usr/local/bin/{prometheus,promtool}
            log_success "Prometheus安装完成"
        fi
    fi
    
    # 安装Node Exporter
    if [[ -f node_exporter-*.tar.gz ]]; then
        log_info "安装Node Exporter..."
        tar -xzf node_exporter-*.tar.gz
        local node_exporter_dir=$(find . -maxdepth 1 -type d -name "node_exporter-*" | head -1)
        if [[ -n "$node_exporter_dir" ]]; then
            cp "$node_exporter_dir"/node_exporter /usr/local/bin/
            chmod +x /usr/local/bin/node_exporter
            log_success "Node Exporter安装完成"
        fi
    fi
    
    # 安装Alertmanager
    if [[ -f alertmanager-*.tar.gz ]]; then
        log_info "安装Alertmanager..."
        tar -xzf alertmanager-*.tar.gz
        local alertmanager_dir=$(find . -maxdepth 1 -type d -name "alertmanager-*" | head -1)
        if [[ -n "$alertmanager_dir" ]]; then
            cp "$alertmanager_dir"/{alertmanager,amtool} /usr/local/bin/
            chmod +x /usr/local/bin/{alertmanager,amtool}
            log_success "Alertmanager安装完成"
        fi
    fi
    
    # 安装Grafana
    if [[ -f grafana-*.tar.gz ]]; then
        log_info "安装Grafana..."
        tar -xzf grafana-*.tar.gz -C /opt/
        local grafana_dir=$(find /opt -maxdepth 1 -type d -name "grafana-*" | head -1)
        if [[ -n "$grafana_dir" ]]; then
            ln -sf "$grafana_dir" /opt/grafana
            chmod +x /opt/grafana/bin/grafana-server
            log_success "Grafana安装完成"
        fi
    fi
    
    # 创建用户
    useradd -r -s /bin/false prometheus 2>/dev/null || true
    useradd -r -s /bin/false grafana 2>/dev/null || true
    
    # 设置权限
    chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus
    chown -R grafana:grafana /etc/grafana /var/lib/grafana /opt/grafana
    
    log_success "二进制文件安装完成"
}

# 应用配置文件
apply_configs() {
    log_info "应用配置文件..."
    
    if [[ -d "$OFFLINE_DIR/configs" ]]; then
        # 复制Ansible配置
        if [[ -f "$OFFLINE_DIR/configs/ansible.cfg" ]]; then
            mkdir -p /etc/ansible
            cp "$OFFLINE_DIR/configs/ansible.cfg" /etc/ansible/
            log_info "已应用Ansible配置"
        fi
        
        # 复制其他配置文件
        if [[ -d "$OFFLINE_DIR/configs/group_vars" ]]; then
            cp -r "$OFFLINE_DIR/configs/group_vars" /etc/ansible/ 2>/dev/null || true
        fi
        
        if [[ -d "$OFFLINE_DIR/configs/roles" ]]; then
            cp -r "$OFFLINE_DIR/configs/roles" /etc/ansible/ 2>/dev/null || true
        fi
    fi
    
    log_success "配置文件应用完成"
}

# 启用服务
enable_services() {
    log_info "启用系统服务..."
    
    # 启用基础服务
    local services=("firewalld" "sshd" "crond")
    
    for service in "${services[@]}"; do
        if systemctl list-unit-files | grep -q "^$service.service"; then
            systemctl enable "$service" 2>/dev/null || true
            systemctl start "$service" 2>/dev/null || true
            log_info "已启用服务: $service"
        fi
    done
    
    log_success "系统服务配置完成"
}

# 验证安装
verify_installation() {
    log_info "验证安装结果..."
    
    # 检查命令可用性
    local commands=("python3" "pip3" "ansible" "prometheus" "node_exporter")
    
    for cmd in "${commands[@]}"; do
        if command -v "$cmd" &> /dev/null; then
            local version=$("$cmd" --version 2>&1 | head -1 || echo "unknown")
            log_success "$cmd: $version"
        else
            log_warning "$cmd: 未安装或不在PATH中"
        fi
    done
    
    # 检查Python包
    log_info "检查Python包..."
    local python_packages=("flask" "requests" "ansible" "prometheus_client")
    
    for package in "${python_packages[@]}"; do
        if python3 -c "import $package" 2>/dev/null; then
            log_success "Python包 $package: 已安装"
        else
            log_warning "Python包 $package: 未安装"
        fi
    done
    
    log_success "安装验证完成"
}

# 生成安装报告
generate_report() {
    log_info "生成安装报告..."
    
    local report_file="/root/ansible-offline-install-report-${DATE}.txt"
    
    cat > "$report_file" << EOF
# Ansible离线安装报告

## 安装信息
- 安装时间: $(date)
- 安装用户: $(whoami)
- 系统版本: $(cat /etc/redhat-release)
- 内核版本: $(uname -r)

## 安装组件

### 系统包
$(rpm -qa | grep -E "python3|ansible|prometheus|grafana" | sort)

### Python包
$(pip3 list | grep -E "flask|requests|ansible|prometheus")

### 二进制文件
$(ls -la /usr/local/bin/ | grep -E "prometheus|node_exporter|alertmanager")

## 服务状态
$(systemctl list-unit-files | grep -E "prometheus|grafana|node_exporter" || echo "暂无相关服务")

## 备份位置
- 配置备份: $BACKUP_DIR
- 安装日志: $LOG_FILE

## 下一步操作
1. 配置主机清单: /etc/ansible/hosts
2. 运行Ansible playbook进行部署
3. 访问监控界面验证安装

EOF
    
    log_success "安装报告已生成: $report_file"
}

# 恢复在线仓库
restore_online_repos() {
    log_info "恢复在线YUM仓库..."
    
    # 恢复原有仓库配置
    if [[ -d "$BACKUP_DIR/yum.repos.d.backup" ]]; then
        rm -f /etc/yum.repos.d/*.repo
        cp "$BACKUP_DIR/yum.repos.d.backup"/*.repo.disabled /etc/yum.repos.d/ 2>/dev/null || true
        
        # 重命名回原来的名字
        for file in /etc/yum.repos.d/*.repo.disabled; do
            if [[ -f "$file" ]]; then
                mv "$file" "${file%.disabled}"
            fi
        done
        
        yum clean all
        log_success "在线仓库已恢复"
    else
        log_warning "未找到仓库备份，请手动恢复在线仓库"
    fi
}

# 显示使用帮助
show_help() {
    cat << EOF
使用方法: $0 [选项]

选项:
  -h, --help              显示此帮助信息
  -f, --full              完整安装（默认）
  -q, --quick             快速安装（跳过验证）
  --skip-backup           跳过配置备份
  --skip-verify           跳过安装验证
  --restore-repos         仅恢复在线仓库
  --dry-run               模拟运行（不执行实际安装）

示例:
  $0                      # 完整离线安装
  $0 -q                   # 快速安装
  $0 --restore-repos      # 恢复在线仓库
  $0 --dry-run            # 模拟运行
EOF
}

# 主函数
main() {
    local full_install=true
    local quick_install=false
    local skip_backup=false
    local skip_verify=false
    local restore_repos=false
    local dry_run=false
    
    # 解析命令行参数
    while [[ $# -gt 0 ]]; do
        case $1 in
            -h|--help)
                show_help
                exit 0
                ;;
            -f|--full)
                full_install=true
                shift
                ;;
            -q|--quick)
                quick_install=true
                full_install=false
                shift
                ;;
            --skip-backup)
                skip_backup=true
                shift
                ;;
            --skip-verify)
                skip_verify=true
                shift
                ;;
            --restore-repos)
                restore_repos=true
                shift
                ;;
            --dry-run)
                dry_run=true
                shift
                ;;
            *)
                log_error "未知选项: $1"
                show_help
                exit 1
                ;;
        esac
    done
    
    # 仅恢复仓库
    if [[ "$restore_repos" == "true" ]]; then
        check_root
        restore_online_repos
        exit 0
    fi
    
    # 模拟运行
    if [[ "$dry_run" == "true" ]]; then
        log_info "=== 模拟运行模式 ==="
        log_info "将执行以下步骤:"
        log_info "1. 检查系统和离线包"
        log_info "2. 备份现有配置"
        log_info "3. 配置本地YUM仓库"
        log_info "4. 安装RPM包"
        log_info "5. 安装Python包"
        log_info "6. 安装二进制文件"
        log_info "7. 应用配置文件"
        log_info "8. 启用系统服务"
        log_info "9. 验证安装"
        log_info "10. 生成安装报告"
        log_info "=== 模拟运行结束 ==="
        exit 0
    fi
    
    # 开始安装流程
    log_info "开始Ansible离线安装..."
    log_info "安装模式: $([ "$full_install" == "true" ] && echo "完整安装" || echo "快速安装")"
    
    # 执行安装步骤
    check_root
    check_system
    
    if [[ "$quick_install" == "false" ]]; then
        check_offline_packages
    fi
    
    if [[ "$skip_backup" == "false" ]]; then
        backup_configs
    fi
    
    setup_local_repo
    install_rpms
    install_python_packages
    install_binaries
    apply_configs
    enable_services
    
    if [[ "$skip_verify" == "false" ]]; then
        verify_installation
    fi
    
    generate_report
    
    log_success "Ansible离线安装完成！"
    log_info "请查看安装报告: /root/ansible-offline-install-report-${DATE}.txt"
    log_info "配置备份位置: $BACKUP_DIR"
    
    # 提示下一步操作
    cat << EOF

=== 下一步操作 ===
1. 配置Ansible主机清单
2. 运行部署命令:
   cd /root/ansible-project
   ansible-playbook -i inventories/production playbooks/site.yml

3. 如需恢复在线仓库，运行:
   $0 --restore-repos
EOF
}

# 脚本入口
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@" 2>&1 | tee "$LOG_FILE"
fi